unit DbEntities;

//{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type
  TDbEntityType =
    (
      detUnknown
      , detDatabase
      , detUser
      , detDbRole
      , detServerRole
      , detColumn
      , detTable
      , detView
      , detUDF
      , detProc
    );

type
  TLineDecorator=record
    Prefix: string;      // prefix to add to next line
    Suffix: string;      // suffix to add to next line
    CurrIndent: string;  // indentation to be used for the immediate line
    NextIndent: string;  // indentation to be used if the next line generates child line(s)
    PreSeparator: string;
    PostSeparator: string;

    function Decorate(const ALine: string): string; overload;
    function Decorate(const ALine: string; const AWithSeparator: Boolean): string; overload;
    procedure Assign(ASrc: TLineDecorator; const AIncrementIndent: Boolean=False);
    procedure Setup(const APrefix, ASuffix, ACurrIndent, ANextIndent: string); overload;
    procedure Setup(const APreSeparator, APostSeparator: string); overload;

    function ChildDecorator: TLineDecorator;
    procedure IncIndent;
    procedure DecIndent;
  end;

type
  TColType=class;
  TDbTable=class;
  TDbTables=class;
  TDbView=class;
  TDbViews=class;
  TDbUDF=class;
  TDbUDFs=class;
  TDbProc=class;
  TDbProcs=class;
  TDbIndex=class;
  TDbIndexes=class;
  TDbIndexCol=class;
  TDbIndexCols=class;
  TDbColumn=class;
  TDbColumns=class;

  { TDbEntity }

  TDbEntity=class
  private
    FName: string;
  protected
    function GetDDL: string; virtual; abstract;
    function GetCreateDDL: string; virtual; abstract;
    function GetDropDDL: string; virtual; abstract;
    function GetFullname: string; virtual;
    class function GetType: TDbEntityType; virtual; abstract;
  public
    constructor Create(const AName: string); virtual;

    property Name     : string read FName write FName;
    property Fullname : string read GetFullname;
    property DDL      : string read GetDDL;
    property CreateDDL: string read GetCreateDDL;
    property DropDDL  : string read GetDropDDL;
    property Type_    : TDbEntityType read GetType;
  end;

  { TDbSimpleEntity }

  TDbSimpleEntity=class(TDbEntity)
  private
    FCreateDDL: string;
  protected
    function GetDDL: string; override;
    function GetCreateDDL: string; override;
    procedure SetCreateDDL(const ADDL: string); virtual;
  public
    property CreateDDL: string read GetCreateDDL write SetCreateDDL;
  end;

  TDbEntityClass = class of TDbEntity;

  { TDbEntities }

  TDbEntities=class
  private
    FIsOwnItems: Boolean;
    // TStringList is used instead of TObjectList or TList, since mostly we
    // will search using names, and must be insensitive. While the original
    // names must be preserved.
    FItems: TStringList;
    function GetCount: Integer;
  protected
    function GetItem(Index: Integer): TDbEntity;
    class function EntityClass: TDbEntityClass; virtual;
    function CreateItem(const AName: string): TDbEntity; virtual;
  public
    constructor Create(const AOwnItems: Boolean=False);
    destructor Destroy; override;

    procedure Clear;
    function Add(const AName: string=''): TDbEntity; overload;
    function Add(AEntity: TDbEntity): Integer; overload;
    function Extract(const AIndex: Integer): TDbEntity; overload;
    function Extract(AEntity: TDbEntity): Boolean; overload;
    procedure Delete(const AIndex: Integer); overload;
    function Delete(AEntity: TDbEntity): Boolean; overload;
    function Find(const AName: string; var AEntity: TDbEntity): Boolean; overload;
    function Find(const AName: string; var AIndex: Integer): Boolean; overload;
    // write DDL of all members
    procedure WriteDDLs(AList: TStrings);

    property Count: Integer read GetCount;
    property Items[Index: Integer]: TDbEntity read GetItem; default;
    property IsOwnItems: Boolean read FIsOwnItems write FIsOwnItems;
  end;

  TDbTableMember=class(TDbEntity)
  private
    FTable: TDbTable;
  public
    constructor Create(ATable: TDbTable; const AName: string); reintroduce; overload; virtual;

    property Table: TDbTable read FTable write FTable;
  end;

  TDbTableMemberClass = class of TDbTableMember;

  TDbTableMembers=class(TDbEntities)
  private
    FTable: TDbTable;
    procedure SetTable(const Value: TDbTable);
  protected
    function GetItem(AIndex: Integer): TDbTableMember;
    class function EntityClass: TDbTableMemberClass; reintroduce; virtual;
    function CreateItem(const AName: string): TDbEntity; override;
  public
    constructor Create(ATable: TDbTable; const AOwnItems: Boolean=True); virtual;

    function Add(const AName: string): TDbTableMember; overload;

    property Table: TDbTable read FTable write SetTable;
    property Items[Index: Integer]: TDbTableMember read GetItem; default;
  end;

  { TColType }

  TColType=class
  protected
    function GetType: Integer; virtual; abstract;
    procedure SetType(const AValue: Integer); virtual; abstract;
    function GetName: string; virtual; abstract;
    procedure SetName(const AName: string); virtual; abstract;
    function GetIsVariableSize: Boolean; virtual; abstract;
    function GetHasPrecision: Boolean; virtual; abstract;
    function GetDefSize: Integer; virtual; abstract;
    function GetDefPrecision: Integer; virtual; abstract;
    function GetMaxLen: Integer; virtual; abstract;
  public
    procedure Assign(ASrc: TColType);

    property Type_: Integer read GetType write SetType;
    property Name: string read GetName write SetName;
    property IsVariableSize: Boolean read GetIsVariableSize;
    property HasPrecision: Boolean read GetHasPrecision;
    property DefSize: Integer read GetDefSize;
    property DefPrecision: Integer read GetDefPrecision;
    property MaxLen: Integer read GetMaxLen;
  end;

  TColTypeClass = class of TColType;

  { TDbColumn }

  TDbColumn=class(TDbTableMember)
  private
    FColType: TColType;
    FIsNullable: Boolean;
    FPrecision: Integer;
    FSize: Integer;
    procedure SetColType(AValue: TColType);
    procedure ColTypeChanged;
  protected
    class function GetType: TDbEntityType; override;
    function GetDDL: string; override;
    function GetCreateDDL: string; override;
    class function GetColTypeClass: TColTypeClass; virtual; abstract;
  public
    constructor Create(ATable: TDbTable; const AColName: string); override;
    constructor Create(ATable: TDbTable; const AColName: string; const AColType: Integer); reintroduce; overload;
    constructor Create(ATable: TDbTable; const AColName: string; const AColType, AColSize: Integer; const AColPrec: Integer=0); reintroduce; overload;
    constructor Create(ATable: TDbTable; const AColName: string; const AColType: Integer; const AIsNullable: Boolean; const AColSize: Integer; const AColPrec: Integer=0); reintroduce; overload;

    destructor Destroy; override;

    property Table;
    property ColType: TColType read FColType write SetColType;
    property Size   : Integer read FSize write FSize;
    property Precision: Integer read FPrecision write FPrecision;
    property IsNullable: Boolean read FIsNullable write FIsNullable;
  end;

  TDbColumnClass = class of TDbColumn;

  { TDbColumns }

  TDbColumns=class(TDbTableMembers)
  private
    function GetDDL: string;
  protected
    function GetItem(Index: Integer): TDbColumn;
    class function EntityClass: TDbTableMemberClass; override;
  public
    function Add(const AName: string=''): TDbColumn; overload;
    function Add(ACol: TDbColumn): Integer; overload;
    function Extract(ACol: TDbColumn): Boolean;
    function Delete(ACol: TDbColumn): Boolean;
    procedure WriteDdlTo(AStrings: TStrings);

    property Table;
    property Items[Index: Integer]: TDbColumn read GetItem; default;
    property DDL: string read GetDDL;
  end;

  TDbIndexType =
    (
      itUnknown
      , itIndex
      , itPrimary
      , itUnique
    );

  TDbIndex=class(TDbTableMember)
  private
    FCols: TDbIndexCols;
    FIndexType: TDbIndexType;
  protected
    function GetDDL: string; override;
    function GetCreateDDL: string; override;
  public
    constructor Create(ATable: TDbTable; const AName: string); override;
    destructor Destroy; override;

    property Table;
    property Cols: TDbIndexCols read FCols;
    property IndexType: TDbIndexType read FIndexType write FIndexType;
  end;

  TDbIndexClass = class of TDbIndex;

  TDbIndexes=class(TDbTableMembers)
  protected
    function GetItem(AIndex: Integer): TDbIndex;
    class function EntityClass: TDbTableMemberClass; override;
  public
    function Add(const AName: string): TDbIndex; overload;

    property Table;
    property Items[Index: Integer]: TDbIndex read GetItem; default;
  end;

  TDbIndexCol=class(TDbEntity)
  private
    FIsAscending: Boolean;
    FIndex: TDbIndex;
  protected
    function GetDDL: string; override;
    function GetCreateDDL: string; override;
  public
    constructor Create(AIndex: TDbIndex; const AName: string); reintroduce;

    property Index_: TDbIndex read FIndex write FIndex;
    property IsAscending: Boolean read FIsAscending write FIsAscending default True;
  end;

  TDbIndexColClass = class of TDbIndexCol;

  TDbIndexCols=class(TDbEntities)
  private
    FIndex: TDbIndex;
    procedure SetIndex(const Value: TDbIndex);
  protected
    function GetItem(Index: Integer): TDbIndexCol;
    function CreateItem(const AName: string): TDbEntity; override;
    class function EntityClass: TDbIndexColClass; reintroduce; virtual;
  public
    function Add(const AName: string=''): TDbIndexCol; overload;
    constructor Create(AIndex: TDbIndex; const AOwnItems: Boolean=True);

    property Index_: TDbIndex read FIndex write SetIndex;
    property Items[Index: Integer]: TDbIndexCol read GetItem; default;
  end;

  TDbForeignKeyRef=class
  private
    FEntityName: string;
    FCols: TStrings;
  public
    constructor Create; overload;
    constructor Create(const AEntityName, ACols: string); overload;
    destructor Destroy; override;

    property EntityName: string read FEntityName write FEntityName;
    property Cols: TStrings read FCols;
  end;

  TDbForeignKey=class(TDbTableMember)
  private
    FRef: TDbForeignKeyRef;
    FCols: TStrings;
  public
    constructor Create(ATable: TDbTable; const AName: string); override;
    destructor Destroy; override;

    property Table;
    property Cols : TStrings read FCols;
    property Ref  : TDbForeignKeyRef read FRef;
  end;

  TDbForeignKeyClass=class of TDbForeignKey;

  TDbForeignKeys=class(TDbTableMembers)
  protected
    class function EntityClass: TDbTableMemberClass; override;
    function GetItem(AIndex: Integer): TDbForeignKey;
  public
    function Add(const AName: string=''): TDbForeignKey; overload;

    property Table;

    property Items[Index: Integer]: TDbForeignKey read GetItem; default;
  end;

  { TDbTable }

  TDbTable=class(TDbEntity)
  protected
    class function GetType: TDbEntityType; override;
    function GetCreateDDL: string; override;
    function GetDropDDL: string; override;

    function GetColumns: TDbColumns; virtual; abstract;
    function GetForeignKeys: TDbForeignKeys; virtual; abstract;
  public
    property Columns: TDbColumns read GetColumns;
    property ForeignKeys: TDbForeignKeys read GetForeignKeys;
  end;

  TDbTableClass = class of TDbTable;

  { TDbTables }

  TDbTables=class(TDbEntities)
  protected
    function GetItem(Index: Integer): TDbTable;
  public
    function Add(const AName: string=''): TDbTable; overload;
    function Add(AEntity: TDbTable): Integer; overload;
    function Extract(AEntity: TDbTable): Boolean;
    function Delete(AEntity: TDbTable): Boolean;

    property Items[Index: Integer]: TDbTable read GetItem; default;
  end;

  { TDbView }

  TDbView=class(TDbSimpleEntity)
  protected
    function GetDropDDL: string; override;
    class function GetType: TDbEntityType; override;
  end;

  TDbViewClass = class of TDbView;

  { TDbViews }

  TDbViews=class(TDbEntities)
  protected
    function GetItem(Index: Integer): TDbView;
  public
    function Add(const AName: string=''): TDbView; overload;
    function Add(AEntity: TDbView): Integer; overload;
    function Extract(AEntity: TDbView): Boolean;
    function Delete(AEntity: TDbView): Boolean;

    property Items[Index: Integer]: TDbView read GetItem; default;
  end;

  { TDbUDF }

  TDbUDF=class(TDbSimpleEntity)
  protected
    function GetDropDDL: string; override;
    class function GetType: TDbEntityType; override;
  end;

  { TDbUDFs }

  TDbUDFs=class(TDbEntities)
  protected
    function GetItem(Index: Integer): TDbUDF;
  public
    function Add(AUdf: TDbUDF): Integer; overload;
    function Extract(AUdf: TDbUDF): Boolean;
    function Delete(AUdf: TDbUDF): Boolean;

    property Items[Index: Integer]: TDbUDF read GetItem; default;
  end;

  { TDbProc }

  TDbProc=class(TDbSimpleEntity)
  protected
    function GetDropDDL: string; override;
    class function GetType: TDbEntityType; override;
  end;

  { TDbProcs }

  TDbProcs=class(TDbEntities)
  protected
    function GetItem(Index: Integer): TDbProc;
  public
    function Add(AProc: TDbProc): Integer; overload;
    function Extract(AProc: TDbProc): Boolean;
    function Delete(AProc: TDbProc): Boolean;

    property Items[Index: Integer]: TDbProc read GetItem; default;
  end;

  { TDbDatabase }

  TDbDatabase=class(TDbSimpleEntity)
  protected
    function GetDropDDL: string; override;
    class function GetType: TDbEntityType; override;

    function GetTables: TDbTables; virtual; abstract;
    function GetViews : TDbViews; virtual; abstract;
    function GetUDFs  : TDbUDFs; virtual; abstract;
    function GetProcs : TDbProcs; virtual; abstract;
  public
    property Tables: TDbTables read GetTables;
    property Views : TDbViews read GetViews;
    property UDFs  : TDbUDFs read GetUDFs;
    property Procs : TDbProcs read GetProcs;
  end;

  TDbHostInfo=record
    Host: string;
    DbName: string;
    UsrName: string;
    UsrPass: string;
  end;

  TDbParsingOption=
    (
      dpoParseDatabase
      , dpoParseTables
      , dpoParseViews
      , dpoParseUDFs
      , dpoParseProcs
      , dpoParseToSingleFile
      , dpoParseToFolder

    );

  TDbParsingOptions = set of TDbParsingOption;

  { TDbParser }

  TDbParser=class
  public
    procedure Parse(const AOptions: TDbParsingOptions;
      ADbInfo: TDbHostInfo; var ADatabase: TDbDatabase); virtual; abstract;

    procedure GetDropTableCmdOf(ATable: TDbTable; ADest: TStrings; const AWithCheckExistence: Boolean=False); virtual; abstract;

    function GetCreateDDLs(ADatabase: TDbDatabase): string; overload; virtual; abstract;
    procedure GetCreateDDLs(ADatabase: TDbDatabase; ADest: TStrings; const APrefix: string=''); overload; virtual; abstract;
    function GetCreateDDLs(ATable: TDbTable): string; overload; virtual; abstract;
    procedure GetCreateDDLs(ATable: TDbTable; ADest: TStrings;
      const APrefix: string='';
      const AIndent: string=''); overload; virtual; abstract;
    function GetCreateDDLs(ASimpleEntity: TDbSimpleEntity): string; overload;
    procedure GetCreateDDLs(ASimpleEntity: TDbSimpleEntity; ADest: TStrings); overload;
    function GetCreateDDLs(AProc: TDbProc): string; overload;
    procedure GetCreateDDLs(AProc: TDbProc; ADest: TStrings); overload;
    function GetCreateDDLs(AUDF: TDbUDF): string; overload;
    procedure GetCreateDDLs(AUDF: TDbUDF; ADest: TStrings); overload;
  end;

  TDbParserClass = class of TDbParser;

const
  SORT_ORDER_CODES: array[Boolean] of string = ('DESC', 'ASC');
  NULLABLE_CODES  : array[Boolean] of string = ('NOT NULL', 'NULL');


implementation

{ TDbDatabase }

function TDbDatabase.GetDropDDL: string;
begin
  Result := 'DROP DATABASE ' + Name;
end;

class function TDbDatabase.GetType: TDbEntityType;
begin
  Result := detDatabase;
end;

{ TDbProc }

function TDbProc.GetDropDDL: string;
begin
  Result := 'DROP PROCEDURE ' + Name;
end;

class function TDbProc.GetType: TDbEntityType;
begin
  Result := detProc;
end;

{ TDbUDF }

function TDbUDF.GetDropDDL: string;
begin
  Result := 'DROP FUNCTION ' + Name;
end;

class function TDbUDF.GetType: TDbEntityType;
begin
  Result := detUDF;
end;

{ TDbView }

function TDbView.GetDropDDL: string;
begin
  Result := 'DROP VIEW ' + Name;
end;

class function TDbView.GetType: TDbEntityType;
begin
  Result := detView;
end;

{ TDbSimpleEntity }

function TDbSimpleEntity.GetDDL: string;
begin
  Result := FCreateDDL;
end;

function TDbSimpleEntity.GetCreateDDL: string;
begin
  Result := FCreateDDL;
end;

procedure TDbSimpleEntity.SetCreateDDL(const ADDL: string);
begin
  FCreateDDL:=ADDL;
end;

{ TDbTable }

class function TDbTable.GetType: TDbEntityType;
begin
  Result := detTable;
end;

function TDbTable.GetCreateDDL: string;
var
  i: Integer;
  vColsDDL: TStringList;
begin
  vColsDDL := TStringList.Create;
  try
    Columns.WriteDdlTo(vColsDDL);
    if vColsDDL.Count > 0 then
      vColsDDL[0] := '	' + vColsDDL[0];

    for i := 1 to vColsDDL.Count-1 do
      vColsDDL[i] := '	, ' + vColsDDL[i];

    Result := 'CREATE TABLE ' + Fullname + '('#13#10
              + vColsDDL.Text
              + ')';
  finally
    vColsDDL.Free;
  end;
end;

function TDbTable.GetDropDDL: string;
begin
  Result := 'DROP TABLE ' + Name;
end;

{ TDbColumns }

function TDbColumns.GetDDL: string;
var
  i: Integer;
  T: TStringList;
begin
  Result := '';
  if Count < 1 then Exit;

  T := TStringList.Create;
  try
    for i := 0 to FItems.Count-2 do
      T.Add(Items[i].DDL +',');
    T.Add(Items[FItems.Count-1].DDL);
    Result := T.Text;
  finally
    T.Free;
  end;
end;

function TDbColumns.GetItem(Index: Integer): TDbColumn;
begin
  Result := TDbColumn(inherited GetItem(Index));
end;

procedure TDbColumns.WriteDdlTo(AStrings: TStrings);
var
  i: Integer;
begin
  for i := 0 to Count-1 do
    AStrings.Add(Items[i].DDL);
end;

function TDbColumns.Add(const AName: string): TDbColumn;
begin
  Result := TDbColumn(inherited Add(AName));
end;

function TDbColumns.Add(ACol: TDbColumn): Integer;
begin
  Result := inherited Add(ACol);
end;

class function TDbColumns.EntityClass: TDbTableMemberClass;
begin
  Result := TDbColumn;
end;

function TDbColumns.Extract(ACol: TDbColumn): Boolean;
begin
  Result := inherited Extract(ACol);
end;

function TDbColumns.Delete(ACol: TDbColumn): Boolean;
begin
  Result := inherited Delete(ACol);
end;

{ TDbProcs }

function TDbProcs.GetItem(Index: Integer): TDbProc;
begin
  Result := TDbProc(inherited GetItem(Index));
end;

function TDbProcs.Add(AProc: TDbProc): Integer;
begin
  Result := inherited Add(AProc);
end;

function TDbProcs.Extract(AProc: TDbProc): Boolean;
begin
  Result := inherited Extract(AProc);
end;

function TDbProcs.Delete(AProc: TDbProc): Boolean;
begin
  Result := inherited Delete(AProc);
end;

{ TDbUDFs }

function TDbUDFs.GetItem(Index: Integer): TDbUDF;
begin
  Result := TDbUDF(inherited GetItem(Index));
end;

function TDbUDFs.Add(AUdf: TDbUDF): Integer;
begin
  Result := inherited Add(AUdf);
end;

function TDbUDFs.Extract(AUdf: TDbUDF): Boolean;
begin
  Result := inherited Extract(AUdf);
end;

function TDbUDFs.Delete(AUdf: TDbUDF): Boolean;
begin
  Result := inherited Delete(AUdf);
end;

{ TDbViews }

function TDbViews.GetItem(Index: Integer): TDbView;
begin
  Result := TDbView(inherited GetItem(Index));
end;

function TDbViews.Add(const AName: string): TDbView;
begin
  Result := TDbView(inherited Add(AName));
end;

function TDbViews.Add(AEntity: TDbView): Integer;
begin
  Result := inherited Add(AEntity);
end;

function TDbViews.Extract(AEntity: TDbView): Boolean;
begin
  Result := inherited Extract(AEntity);
end;

function TDbViews.Delete(AEntity: TDbView): Boolean;
begin
  Result := inherited Delete(AEntity);
end;

{ TDbTables }

function TDbTables.GetItem(Index: Integer): TDbTable;
begin
  Result := TDbTable(inherited GetItem(Index));
end;

function TDbTables.Add(const AName: string): TDbTable;
begin
  Result := TDbTable(inherited Add(AName));
end;

function TDbTables.Add(AEntity: TDbTable): Integer;
begin
  Result := inherited Add(AEntity);
end;

function TDbTables.Extract(AEntity: TDbTable): Boolean;
begin
  Result := inherited Extract(AEntity);
end;

function TDbTables.Delete(AEntity: TDbTable): Boolean;
begin
  Result := inherited Delete(AEntity);
end;

{ TColType }

procedure TColType.Assign(ASrc: TColType);
begin
  if ASrc <> Self then
  begin
    if ASrc=nil then
      Self.Type_ := 0
    else begin
      Self.SetType(ASrc.Type_);
    end;
  end;
end;

{ TDbColumn }

procedure TDbColumn.SetColType(AValue: TColType);
begin
  if FColType=AValue then Exit;
  FColType:=AValue;
  ColTypeChanged;
end;

procedure TDbColumn.ColTypeChanged;
begin
  if ColType.IsVariableSize then
  begin
    FSize := ColType.DefSize;
    if ColType.HasPrecision then
      FPrecision:=ColType.DefPrecision
    else
      FPrecision:=0;
  end
  else begin
    FSize:=0;
    FPrecision:=0;
  end;
end;

class function TDbColumn.GetType: TDbEntityType;
begin
  Result := detColumn;
end;

function TDbColumn.GetDDL: string;
var
  vColSize: Integer;
  vColPrec: Integer;
begin
  Result := Format('%s %s', [Fullname, ColType.Name]);
  if ColType.IsVariableSize then
  begin
    vColSize:=Self.Size;
    if vColSize < 1 then
      vColSize := ColType.DefSize;

    if ColType.HasPrecision then
    begin
      vColPrec:=Self.Precision;
      if vColPrec=0 then
        vColPrec:=ColType.DefPrecision;
      Result := Format('%s(%d,%d)', [Result, vColSize, vColPrec]);
    end
    else
      Result := Format('%s(%d)', [Result, vColSize]);
  end;

  if IsNullable then
    Result := Result + ' NULL'
  else
    Result := Result + ' NOT NULL';
end;

function TDbColumn.GetCreateDDL: string;
begin
  Result := GetDDL;
end;

constructor TDbColumn.Create(ATable: TDbTable; const AColName: string);
begin
  inherited Create(AColName);
  FIsNullable := True;
  FTable := ATable;
  FColType := GetColTypeClass.Create;
end;

constructor TDbColumn.Create(ATable: TDbTable; const AColName: string; const AColType: Integer);
begin
  Create(ATable, AColName);
  FColType.Type_:= AColType;
end;

constructor TDbColumn.Create(ATable: TDbTable; const AColName: string; const AColType: Integer;
  const AIsNullable: Boolean; const AColSize: Integer; const AColPrec: Integer);
begin
  Create(ATable, AColName, AColType, AColSize, AColPrec);
  FIsNullable:=AIsNullable;
end;

constructor TDbColumn.Create(ATable: TDbTable; const AColName: string; const AColType,
  AColSize: Integer; const AColPrec: Integer);
begin
  Create(ATable, AColName, AColType);
  FSize:=AColSize;
  FPrecision:=AColPrec;
end;

destructor TDbColumn.Destroy;
begin
  FColType.Free;
  inherited Destroy;
end;

{ TDbEntity }

constructor TDbEntity.Create(const AName: string);
begin
  FName := AName;
end;

function TDbEntity.GetFullname: string;
begin
  Result := Name;
end;

{ TDbEntities }

function TDbEntities.GetCount: Integer;
begin
  Result := FItems.Count;
end;

function TDbEntities.GetItem(Index: Integer): TDbEntity;
begin
  Result := FItems.Objects[Index] as TDbEntity;
end;

procedure TDbEntities.WriteDDLs(AList: TStrings);
var
  i: Integer;
begin
  for i := 0 to Count-1 do
    AList.AddObject(Items[i].DDL, Items[i]);
end;

class function TDbEntities.EntityClass: TDbEntityClass;
begin
  Result := nil;
end;

function TDbEntities.CreateItem(const AName: string): TDbEntity;
begin
  Result := EntityClass.Create(AName);
  Add(Result);
end;

constructor TDbEntities.Create(const AOwnItems: Boolean);
begin
  FIsOwnItems:=AOwnItems;
  FItems := TStringList.Create;
  FItems.Duplicates:=dupError;
//  FItems.Sorted:=True;
end;

destructor TDbEntities.Destroy;
begin
  Clear;
  FItems.Free;
  inherited Destroy;
end;

procedure TDbEntities.Clear;
var
  i: Integer;
begin
  if IsOwnItems then
    for i := 0 to FItems.Count-1 do
      FItems.Objects[i].Free;
  FItems.Clear;
end;

function TDbEntities.Add(const AName: string): TDbEntity;
begin
  Result := CreateItem(AName);
end;

function TDbEntities.Add(AEntity: TDbEntity): Integer;
begin
  Result := FItems.AddObject(LowerCase(AEntity.Name), AEntity);
end;

function TDbEntities.Extract(const AIndex: Integer): TDbEntity;
begin
  Result := TDbEntity(FItems[AIndex]);
  FItems.Delete(AIndex);
end;

function TDbEntities.Extract(AEntity: TDbEntity): Boolean;
var
  i: Integer;
begin
  i := FItems.IndexOf(LowerCase(AEntity.Name));
  Result := i > -1;
  if Result then
    Extract(i);
end;

procedure TDbEntities.Delete(const AIndex: Integer);
begin
  if IsOwnItems then
    FItems.Objects[AIndex].Free;
  FItems.Delete(AIndex);
end;

function TDbEntities.Delete(AEntity: TDbEntity): Boolean;
var
  i: Integer;
begin
  i := FItems.IndexOf(LowerCase(AEntity.Name));
  Result := i > -1;
  if Result then
  begin
    if IsOwnItems then
      FItems.Objects[i].Free;
    FItems.Delete(i);
  end;
end;

function TDbEntities.Find(const AName: string; var AEntity: TDbEntity): Boolean;
var
  i: Integer;
begin
  i := FItems.IndexOf(LowerCase(AName));
  Result := i > -1;
  if Result then
    AEntity := Items[i]
  else
    AEntity := nil;
end;

function TDbEntities.Find(const AName: string; var AIndex: Integer): Boolean;
begin
  AIndex := FItems.IndexOf(LowerCase(AName));
  Result := AIndex > -1;
end;

{ TIndexCol }

constructor TDbIndexCol.Create(AIndex: TDbIndex; const AName: string);
begin
  inherited Create(AName);
  FIndex := AIndex;
  FIsAscending := True;
end;

function TDbIndexCol.GetCreateDDL: string;
begin
  Result := GetDDL;
end;

function TDbIndexCol.GetDDL: string;
begin
  Result := Name + ' ' + SORT_ORDER_CODES[IsAscending];
end;

{ TIndexCols }

function TDbIndexCols.Add(const AName: string): TDbIndexCol;
begin
  Result := TDbIndexCol(inherited Add(AName));
end;

constructor TDbIndexCols.Create(AIndex: TDbIndex; const AOwnItems: Boolean);
begin
  inherited Create(AOwnItems);
  FIndex := AIndex;
end;

function TDbIndexCols.CreateItem(const AName: string): TDbEntity;
begin
  Result := EntityClass.Create(Index_, AName);
  Add(Result);
end;

class function TDbIndexCols.EntityClass: TDbIndexColClass;
begin
  Result := TDbIndexCol;
end;

function TDbIndexCols.GetItem(Index: Integer): TDbIndexCol;
begin
  Result := TDbIndexCol(inherited GetItem(Index));
end;

procedure TDbIndexCols.SetIndex(const Value: TDbIndex);
var
  i: Integer;
begin
  if FIndex <> Value then
    for i := 0 to Count-1 do
      Items[i].Index_ := FIndex;
end;

{ TDbIndexes }

function TDbIndexes.Add(const AName: string): TDbIndex;
begin
  Result := TDbIndex(inherited Add(AName));
end;

class function TDbIndexes.EntityClass: TDbTableMemberClass;
begin
  Result := TDbIndex;
end;

function TDbIndexes.GetItem(AIndex: Integer): TDbIndex;
begin
  Result := TDbIndex(inherited GetItem(AIndex));
end;

{ TDbIndex }

constructor TDbIndex.Create(ATable: TDbTable; const AName: string);
begin
  inherited Create(ATable, AName);
  FCols := TDbIndexCols.Create(Self, True);
end;

destructor TDbIndex.Destroy;
begin
  FCols.Free;
  inherited;
end;

function TDbIndex.GetCreateDDL: string;
begin
  Result := GetDDL;
end;

function TDbIndex.GetDDL: string;
begin
end;

{ TDbParser }

function TDbParser.GetCreateDDLs(ASimpleEntity: TDbSimpleEntity): string;
begin
  Result := ASimpleEntity.CreateDDL;
end;

procedure TDbParser.GetCreateDDLs(ASimpleEntity: TDbSimpleEntity;
  ADest: TStrings);
begin
  ADest.Add(ASimpleEntity.CreateDDL);
end;

procedure TDbParser.GetCreateDDLs(AProc: TDbProc; ADest: TStrings);
begin
  ADest.Add(AProc.CreateDDL);
end;

function TDbParser.GetCreateDDLs(AProc: TDbProc): string;
begin
  Result := AProc.CreateDDL;
end;

procedure TDbParser.GetCreateDDLs(AUDF: TDbUDF; ADest: TStrings);
begin
  ADest.Add(AUDF.CreateDDL);
end;

function TDbParser.GetCreateDDLs(AUDF: TDbUDF): string;
begin
  Result := AUDF.CreateDDL;
end;

{ TLineDecorator }

procedure TLineDecorator.Assign(ASrc: TLineDecorator;
  const AIncrementIndent: Boolean);
begin
  Self.Prefix := ASrc.Prefix;
  Self.Suffix := ASrc.Suffix;
  if AIncrementIndent then
    Self.CurrIndent := ASrc.CurrIndent + ASrc.NextIndent
  else
    Self.CurrIndent := ASrc.CurrIndent;
  Self.NextIndent := ASrc.NextIndent;
  Self.PreSeparator := ASrc.PreSeparator;
  Self.PostSeparator := ASrc.PostSeparator;
end;

function TLineDecorator.Decorate(const ALine: string): string;
begin
  Result := CurrIndent + Prefix + ALine + Suffix;
end;

procedure TLineDecorator.Setup(const APrefix, ASuffix, ACurrIndent,
  ANextIndent: string);
begin
  Prefix := APrefix;
  Suffix := ASuffix;
  CurrIndent := ACurrIndent;
  NextIndent := ANextIndent;
end;

function TLineDecorator.ChildDecorator: TLineDecorator;
begin
  Result.Assign(Self, True);
end;

procedure TLineDecorator.DecIndent;
begin
  Delete(CurrIndent, 1, Length(NextIndent));
end;

function TLineDecorator.Decorate(const ALine: string;
  const AWithSeparator: Boolean): string;
begin
  if AWithSeparator then
    Result := CurrIndent + PreSeparator + ALine + PostSeparator
  else
    Result := CurrIndent + Prefix + ALine + Suffix;
end;

procedure TLineDecorator.IncIndent;
begin
  CurrIndent := CurrIndent + NextIndent;
end;

procedure TLineDecorator.Setup(const APreSeparator, APostSeparator: string);
begin
  PreSeparator := APreSeparator;
  PostSeparator := APostSeparator;
end;

{ TDbForeignKeyRef }

constructor TDbForeignKeyRef.Create;
begin
  FCols := TStringList.Create;
end;

constructor TDbForeignKeyRef.Create(const AEntityName, ACols: string);
begin
  Create;
  FEntityName := AEntityName;
  FCols.CommaText := ACols;
end;

destructor TDbForeignKeyRef.Destroy;
begin
  FCols.Free;
  inherited;
end;

{ TDbForeignKey }

constructor TDbForeignKey.Create(ATable: TDbTable; const AName: string);
begin
  inherited Create(AName);
  FTable := ATable;
  FCols := TStringList.Create;
  FRef  := TDbForeignKeyRef.Create;
end;

destructor TDbForeignKey.Destroy;
begin
  FRef.Free;
  FCols.Free;
  inherited;
end;

{ TDbForeignKeys }

function TDbForeignKeys.Add(const AName: string): TDbForeignKey;
begin
  Result := TDbForeignKey(inherited Add(AName));
end;

class function TDbForeignKeys.EntityClass: TDbTableMemberClass;
begin
  Result := TDbForeignKey;
end;

function TDbForeignKeys.GetItem(AIndex: Integer): TDbForeignKey;
begin
  Result := TDbForeignKey(inherited GetItem(AIndex));
end;

{ TDbTableMember }

constructor TDbTableMember.Create(ATable: TDbTable; const AName: string);
begin
  inherited Create(AName);
  FTable := ATable;
end;

{ TDbTableMembers }

function TDbTableMembers.Add(const AName: string): TDbTableMember;
begin
  Result := TDbTableMember(inherited Add(AName));
end;

constructor TDbTableMembers.Create(ATable: TDbTable; const AOwnItems: Boolean);
begin
  inherited Create(AOwnItems);
  FTable := ATable;
end;

function TDbTableMembers.CreateItem(const AName: string): TDbEntity;
begin
  Result := EntityClass.Create(Self.Table, AName);
  Add(Result);
end;

class function TDbTableMembers.EntityClass: TDbTableMemberClass;
begin
  Result := TDbTableMember;
end;

function TDbTableMembers.GetItem(AIndex: Integer): TDbTableMember;
begin
  Result := TDbTableMember(inherited GetItem(AIndex));
end;

procedure TDbTableMembers.SetTable(const Value: TDbTable);
var
  i: Integer;
begin
  if FTable <> Value then
  begin
    FTable := Value;
    for i := 0 to Count-1 do
      Items[i].Table := FTable;
  end;
end;

end.

